package com.um.springboot.starter.utils;

import com.um.springboot.starter.code.CommonCode;
import com.um.springboot.starter.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.util.ResourceUtils;

import javax.crypto.Cipher;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author 邓南辉
 * 日期 2021/1/13
 * 版权所有 广州优匠科技有限公司
 */
@Slf4j
public class RSAUtil {

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    private static final KeyPair keyPair = initKey();

    private static KeyPair initKey(){
        try {
            Security.addProvider(new BouncyCastleProvider());
            SecureRandom random = new SecureRandom();
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
            generator.initialize(1024, random);
            return generator.generateKeyPair();
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成public key
     * @return
     */
    public static String generateBase64PublicKey(){
        RSAPublicKey key = (RSAPublicKey)keyPair.getPublic();
        return new String(Base64.encodeBase64(key.getEncoded()));
    }

    /**
     * 生成private key
     * @return
     */
    public static String generateBase64PrivateKey(){
        RSAPrivateKey key = (RSAPrivateKey)keyPair.getPrivate();
        return new String(Base64.encodeBase64(key.getEncoded()));
    }

    /**
     * 从字符串解码私钥
     *
     * @param key 密钥
     * @return base64后的字符串
     * @throws Exception Exception
     */
    public static PrivateKey decodePrivateKey(String key) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(key);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(pkcs8KeySpec);
    }

    /**
     * 从字符串解码公钥
     *
     * @param publicKey 公钥
     * @return 公钥
     * @throws Exception Exception
     */
    public static PublicKey decodePublicKey(String publicKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(x509KeySpec);
    }
    /**
     * RSA加密
     *
     * @param data 待加密数据
     * @param publicKey 公钥
     * @return
     */
    public static String encryptBase64(String data, String publicKey) throws Exception {
        return new String(Base64.encodeBase64String(encrypt(data.getBytes(),publicKey)));
    }

    /**
     * RSA加密
     *
     * @param data 待加密数据
     * @param publicKey 公钥
     * @return
     */
    public static byte[] encrypt(byte[] data,String publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, decodePublicKey(publicKey));
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }


    /**
     * 解密
     * @param string
     * @return
     */
    public static String decryptBase64(String string,String privateKey) {
        return new String(decrypt(Base64.decodeBase64(string),privateKey));
    }

    private static byte[] decrypt(byte[] string,String privateKey) {
        try {
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
            RSAPrivateKey pbk = (RSAPrivateKey)decodePrivateKey(privateKey);
            cipher.init(Cipher.DECRYPT_MODE, pbk);
            byte[] plainText = cipher.doFinal(string);
            return plainText;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Deprecated
    public static String simpleEncrypt(String str){
        try {
            URL resource = ResourceUtils.getURL("classpath:publicKey.key");
            BufferedReader br = new BufferedReader(new InputStreamReader(resource.openStream()));
            String privateKey = br.readLine();
            return new String(encrypt(str.getBytes(), privateKey));
        } catch (Exception e) {
            log.error("RSA加密错误", e);
            throw new ServiceException(CommonCode.RSADecryptError);
        }
    }

    @Deprecated
    public static String simpleEncryptBase64(String str){
        try {
            URL resource = ResourceUtils.getURL("classpath:publicKey.key");
            BufferedReader br = new BufferedReader(new InputStreamReader(resource.openStream()));
            String privateKey = br.readLine();
            return encryptBase64(str, privateKey);
        } catch (Exception e) {
            log.error("RSA加密错误", e);
            throw new ServiceException(CommonCode.RSADecryptError);
        }
    }

    @Deprecated
    public static String simpleDecrypt(String str){
        try {
            URL resource = ResourceUtils.getURL("classpath:privateKey.key");
            BufferedReader br = new BufferedReader(new InputStreamReader(resource.openStream()));
            String privateKey = br.readLine();
            return decryptBase64(str, privateKey);
        } catch (Exception e) {
            log.error("RSA解码错误", e);
            throw new ServiceException(CommonCode.RSADecryptError);
        }
    }

    @Deprecated
    public static String simpleDecryptBase64(String str){
        try {
            str = new String(Base64.decodeBase64(str));
        } catch (Exception e) {
            log.error("Base64解码错误", e);
            throw new ServiceException(CommonCode.Base64DecryptError);
        }
        return simpleDecrypt(str);
    }

    /************************以下为新的加解密方法*************************/
    public static String simpleEncryptNewBase64(String str){
        try {
            URL resource = ResourceUtils.getURL("classpath:publicKey.key");
            BufferedReader br = new BufferedReader(new InputStreamReader(resource.openStream()));
            String privateKey = br.readLine();
            return encryptBase64(str, privateKey);
        } catch (Exception e) {
            log.error("RSA加密错误", e);
            throw new ServiceException(CommonCode.RSADecryptError);
        }
    }

    @Deprecated
    public static String simpleDecryptNewBase64(String str){
        try {
            URL resource = ResourceUtils.getURL("classpath:privateKey.key");
            BufferedReader br = new BufferedReader(new InputStreamReader(resource.openStream()));
            String privateKey = br.readLine();
            return decryptBase64(str, privateKey);
        } catch (Exception e) {
            log.error("RSA解码错误", e);
            throw new ServiceException(CommonCode.RSADecryptError);
        }
    }



    public static void main(String[] args) throws Exception{
        // 生成public key
        //System.out.println(generateBase64PublicKey());
        //System.out.println(generateBase64PrivateKey());

        System.out.println(encryptBase64("Ch18#3Ti@p85!","MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCDeoWrA8EEPpryBkpBHfD0VdzkMpbu3LLHk/znN7L4bBFYAePKvwn64Eby3ZIjAL0++wbgGyRqBGBILeyq6AFId8LJf2eGbQzoopi2Y4qeCYV7uNB5AbRqPTOwMJgyvMpsUjVqVaXgPMoPJctZ+ZffZvBTxq8tKhDMNIsEEZWxbwIDAQAB"));

    }



}
